#!/usr/bin/env python
# -*- coding: gb2312 -*-
# $Id$

import os
import string
import random
import gd
import math
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

from leazy.running.request import Response


SESSION_NAME = "authcode"

# merge digits and ascii_letters, but not include 0, o, O, 1, l, L.
CAP_CHARS = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKMNPQRSTUVWXYZ"

BG_COLOR_RED = 255
BG_COLOR_GREEN = 255
BG_COLOR_BLUE = 255

CODE_COLOR_RED = 180
CODE_COLOR_GREEN = 180
CODE_COLOR_BLUE = 180

DIS_TO_LEFT_RATIO = 0.1
DIS_TO_TOP_RATIO = 0.9
CHAR_SPACING = 1


class ImgCaptchaGenerator(object):
    """
    Generate image captcha using python-gd.
    """
    def __init__(self, key_len=4, image_size=(80, 20), pointsize=12, dot_numbers=30, ttf_font=None):
        self.key_len = key_len
        self.image_size = image_size
        self.pointsize = pointsize
        self.dot_numbers = dot_numbers
        self.ttf_font = ttf_font

        self._image = None
        self._image_color = 0
        self._init()


    def _init(self):
        self._image = gd.image(self.image_size)

        # set backgroud color
        bg_color = self._image.colorAllocate((BG_COLOR_RED, BG_COLOR_GREEN, BG_COLOR_BLUE))
        self._image.fill((0, 0), bg_color)

        self._image_color = self._image.colorAllocate((
            random.randint(0, CODE_COLOR_RED),
            random.randint(0, CODE_COLOR_GREEN),
            random.randint(0, CODE_COLOR_BLUE)
        ))


    def _generate_chars(self, authcode):
        width, height = self.image_size
        char_x = width * DIS_TO_LEFT_RATIO 
        char_y = height * DIS_TO_TOP_RATIO 

        for char in authcode:
            # generate angle randomly between -pi/8 and pi/8 
            angle = random.random() * 2 * math.pi / 8 - math.pi / 8 
            ttf_font = self.ttf_font if isinstance(self.ttf_font, str) \
                    else self.ttf_font[random.randint(0, len(self.ttf_font)-1)]

            drect = self._image.string_ttf(ttf_font, self.pointsize, angle, \
                    (char_x, char_y), char, self._image_color)
            char_x = drect[2] + CHAR_SPACING 


    def _generate_lines(self):
        width, height = self.image_size

        x = random.randint(width / 2, width * 3 / 4)
        y = random.randint(height / 3, height / 2)
        w = width 
        h = random.randint(0, height)
        s = random.randint(0, 30)
        e = random.randint(120, 200)
        self._image.arc((x,y), (w,h), s, e, self._image_color)


    def _generate_dots(self):
        width, height = self.image_size
        for i in range(self.dot_numbers):
            self._image.setPixel((random.randint(0, width), random.randint(0, height)), self._image_color)


    def _fill_border(self):
        width, height = self.image_size

        p1 = (0, 0)
        p2 = (width - 1, 0)
        p3 = (width - 1, height - 1)
        p4 = (0, height - 1)
        self._image.line(p1, p2, self._image_color)
        self._image.line(p2, p3, self._image_color)
        self._image.line(p4, p3, self._image_color)
        self._image.line(p4, p1, self._image_color)


    def generate_captcha(self, **kargs):
        authcode = kargs.get("authcode")
        if authcode is None:
            authcode = "".join(random.sample(CAP_CHARS, self.key_len))

        self._generate_chars(authcode)
        self._generate_lines()
        #self._generate_dots()
        self._fill_border()

        buf = StringIO()
        self._image.writeJpeg(buf)

        image_data = buf.getvalue()
        return authcode, image_data



class Captcha(object):
    def __init__(self, req):
        self._req = req


    def yield_captcha(self, img_captcha_gen):
        authcode, image_data = img_captcha_gen.generate_captcha()

        # create session if no session
        if self._req.SESSION is None:
            self._req.create_session()

        # authcode -> session
        self._req.SESSION[SESSION_NAME] = authcode
        self._req.SESSION.save()

        # captcha -> response
        response = Response(image_data)
        response.set_header("cache-control", "no-cache")
        response.set_header("expires", "0")
        response.set_header("pragma", "no-cache")
        response.set_header("content-type", "image/jpeg")
        return response


    def check(self, input):
        if self._req.SESSION is None:
            return False

        authcode = self._req.SESSION.get(SESSION_NAME)
        if authcode is None or authcode.upper() != input.upper():
            return False

        # remove session, because one image should use once
        del self._req.SESSION[SESSION_NAME]
        return True
